/***************************************************************************
 *   Copyright (C) 2007 by Grzegorz Latarowski, Maciej Gajewski            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

// Qty
#include <QDateTime>
#include <QImage>
#include <QRgb>


// gcommon
#include "gexception.h"

// gnet
#include "autopackets.h"

// local
#include "autoclient.h"



#ifdef Q_OS_UNIX
Q_IMPORT_PLUGIN( gjpeg2kjasper );
#else
Q_IMPORT_PLUGIN( gjpeg2kj2kcodec );
#endif




// ===========================================================================
//                                GAutoClient
// ===========================================================================




// --------------------- GAutoClient::GAutoClient() --------------------------
GAutoClient::GAutoClient( QObject * pParent ) : GNetClient( pParent )
{
		// register packets types used by autonet
	GAutoPacket::registerPackets();
	
	// setup udp receiver
	
	connect( &_udpReceiver, SIGNAL( packetsReceived( const QQueue< QPair<quint16, QByteArray> >& ) ),
				this, SLOT( udpPacketsReceived( const QQueue< QPair<quint16, QByteArray> >& ) ) );
	
	// turn on congestion control
	_udpReceiver.setCongestionEnabled( true );
}
// ---------------------------------------------------------------------------


// ------------------ GAutoClient::~GAutoClient() ----------------------------
GAutoClient::~GAutoClient()
{
	// nothing
}
// ---------------------------------------------------------------------------


// ------------------------- GAutoClient::frame() ----------------------------
QPixmap GAutoClient::frame()
{
	QPixmap frame;
	
	if ( isConnected() )
	{
		// create request packet
		quint32 requestId = getRequestId();
		GAutoRequest request( GAutoRequest::REQUEST_FRAME, requestId );

		// send packet
		sendPacket( request );

		// wait for respones
		GNetPacketReply * pReply = waitForReply( requestId );

		GAutoReplyFrame * pReplyFrame = qobject_cast< GAutoReplyFrame * >(pReply);

		if ( pReplyFrame )
		{
			if ( ! frame.loadFromData( pReplyFrame->image(), "j2c" ) )
			{
				// check for error
				qDebug("Invalid jpeg data, size: %d", pReplyFrame->image().size() );
			}
			// succes 
		}
		
		delete pReply;
	}
	
	return frame;
}
// ---------------------------------------------------------------------------


// ------------------------- GAutoClient::params() ---------------------------
GParams GAutoClient::params()
{
	GParams params;
	
	if ( isConnected() )
	{
		// create request packet
		quint32 requestId = getRequestId();
		GAutoRequest request( GAutoRequest::REQUEST_PARAMS, requestId );

		// send packet
		sendPacket( request );

		// wait for respones
		GNetPacketReply * pReply = waitForReply( requestId );

		GAutoReplyParams * pReplyParams = qobject_cast< GAutoReplyParams * >(pReply);

		if ( pReplyParams )
		{
			params = pReplyParams->params();
		}
		delete pReply;
	}
	
	return params;
}
// ---------------------------------------------------------------------------


// ------------------------- GAutoClient::cameraConfig() ---------------------
QByteArray GAutoClient::cameraConfig()
{
	QByteArray receivedData;
	
	if ( isConnected() )
	{
		// create request packet
		quint32 requestId = getRequestId();
		GAutoRequest request( GAutoRequest::REQUEST_CAMERA_CONFIG, requestId );

		// send packet
		sendPacket( request );

		// wait for respones
		GNetPacketReply * pReply = waitForReply( requestId );

		GAutoReplyCameraConfig * pReplyCameraConfig;
		pReplyCameraConfig = qobject_cast< GAutoReplyCameraConfig * >(pReply);

		if ( pReplyCameraConfig )
		{
			receivedData = pReplyCameraConfig->data();
		}
		delete pReply;
	}
	else
	{
		throw( GSysError( "Not connected to camera." ) );
// 		qDebug("GAutoClient::cameraConfig: not connected" );
	}

	if( receivedData.isEmpty() )
	{
		throw( GConfigurationError( "Failure returning configuration file." ) );
// 		qDebug("GAutoClient::cameraConfig: failure, returning empty array");
	}
	return receivedData;
}
// ---------------------------------------------------------------------------


// ------------------------- GAutoClient::areasConfig() ----------------------
QByteArray GAutoClient::areasConfig()
{
	QByteArray result;
	
	if ( isConnected() )
	{
		// create request packet
		quint32 requestId = getRequestId();
		GAutoRequest request( GAutoRequest::REQUEST_AREAS_CONFIG, requestId );

		// send packet
		sendPacket( request );

		// wait for respones
		GNetPacketReply * pReply = waitForReply( requestId );

		GAutoReplyAreasConfig * pReplyAreasConfig = qobject_cast< GAutoReplyAreasConfig * >(pReply);

		if ( pReplyAreasConfig )
		{
			result =  pReplyAreasConfig->data();
		}
		
		delete pReply;
	}
	
	return result;
}
// ---------------------------------------------------------------------------


// ----------------- GAutoClient::sendAreasConfig() --------------------------
void GAutoClient::sendAreasConfig( QByteArray & fileData )
{
	GAutoAreasConfig areasConfig;
	areasConfig.setData( fileData );
	sendPacket( areasConfig );
}
// ---------------------------------------------------------------------------


// ------------------ GAutoClient::sendMarkersImage() ------------------------
void GAutoClient::sendMarkersImage( pimage_t markersImage )
{
	GAutoMarkersImage markersImagePacket;
	markersImagePacket.setMarkersImage( markersImage );
	sendPacket( markersImagePacket );
}
// ---------------------------------------------------------------------------
	
	
// ------------------- GAutoClient::sendRoiImage() ---------------------------
void GAutoClient::sendRoiImage( pimage_t roiImage )
{
	GAutoRoiImage roiImagePacket;
	roiImagePacket.setRoiImage( roiImage );
	sendPacket( roiImagePacket );
}
// ---------------------------------------------------------------------------
	
	
// ------------------- GAutoClient::sendShutterImage() -------------------
void GAutoClient::sendShutterImage( pimage_t shutterImage )
{
	GAutoShutterImage shutterImagePacket;
	shutterImagePacket.setShutterImage( shutterImage );
	sendPacket( shutterImagePacket );
}
// ---------------------------------------------------------------------------


// -------------------- GAutoClient::sendParams() ----------------------------
void GAutoClient::sendParams( const GParams & params )
{
	GAutoParams paramsPacket;
	paramsPacket.setParams( params );
	sendPacket( paramsPacket );
}
// ---------------------------------------------------------------------------
	


// --------------- GAutoClient::udpPacketReceived() --------------------------
void GAutoClient::udpPacketsReceived( const QQueue< QPair<quint16, QByteArray> >& packets )
{
	// search list from the end, process one packt of each type
	QList<quint16> processedTypes;
	
	for( int i = packets.size() - 1; i >= 0 ; i-- )
	{
		// read last packet data
		QByteArray data = packets[i].second;// tricky: get data from last packet 
		
		QDataStream in( data );
		in.setVersion( QDATASTREAM_VERSION );
		
		
			// read header 
		GHeader header;
		header.read( in );
			// create apropriate packet
		GNetPacket * pPacket = GNetPacketFactory::instance()->createPacket( header );
			// read packet from stream 
		pPacket->read( in );
		
		// check packet type
		if ( ! processedTypes.contains( pPacket->packetType() ) )
		{
			// ok, new type!
			processPacket( pPacket );
			processedTypes.append( pPacket->packetType() );
		}
		
		delete pPacket;
		
	}
}
// ---------------------------------------------------------------------------


// -------------------- GAutoClient::processPacket() -------------------------
/// Prcesses incoming packet
void GAutoClient::processPacket( GNetPacket * pPacket )
{
	if ( pPacket )
	{
		switch ( pPacket->packetType() )
		{
			case GAutoPacket::PACKET_OBJECTS_COORDINATES:
				process_PACKET_OBJECTS_COORDINATES( qobject_cast< GAutoObjectCoordinates * >( pPacket ) );
				break;
				
			case GAutoPacket::PACKET_FRAME:
				process_PACKET_FRAME( qobject_cast< GAutoFrame * >( pPacket ) );
				break;

			case GAutoPacket::PACKET_EVENT_TRIGGER:
				process_PACKET_EVENT_TRIGGER( qobject_cast< GAutoEventTrigger * >( pPacket ) );
				break;

			// ignore others
			default:
				; // nothing
		}
	} // pPacket
}
// ---------------------------------------------------------------------------


// ----------- GAutoClient::process_PACKET_OBJECTS_COORDINATES() -------------
void GAutoClient::process_PACKET_OBJECTS_COORDINATES( GAutoObjectCoordinates * pPacket )
{
	emit newObjectsCoordinates( pPacket->objectsCoordinates() );
}
// ---------------------------------------------------------------------------


// -------------- GAutoClient::process_PACKET_FRAME() ------------------------
void GAutoClient::process_PACKET_FRAME( GAutoFrame * pPacket )
{
		// check pointer
	if( !pPacket ) return;
	
	QPixmap frame;
	
	// decode jpeg frame
	if ( pPacket->type() == GAutoFrame::JPEG2K_YUV )
	{
		if ( ! frame.loadFromData( pPacket->image(), "j2c" ) )
		{
			// check for error
			qDebug("Invalid jpeg data, size: %d", pPacket->image().size() );
		}
	}
	// decode raw image
	else if ( pPacket->type() == GAutoFrame::RAW_IMAGE )
	{
		QByteArray imageData = pPacket->image();
		int width = pPacket->width();
		int height = pPacket->height();
		QImage image( width, height, QImage::Format_RGB32 );

		char* byte = imageData.data();
		for( int y = 0; y < height; y++ )
		{
			for( int x = 0; x< width; x++ )
			{
				if ( byte< imageData.data() + imageData.size()  )
				{
					image.setPixel( x, y, qRgb( *byte, *byte, *byte ) );
					byte++;
				}
				else
				{
					qWarning("image buffer smalles than expected, buf size: %d, image: %d pixels",
								imageData.size(),
								width*height
							);
				}
			}
		}

		// TODO ugly
		// scale if very small (propably segmented)
		if ( width < 100 )
		{
			frame = QPixmap::fromImage( image.scaled( width*8, height*8 ) );

		}
		else
		{
			frame = QPixmap::fromImage( image );
		}
	}
	
	emit newPixmap( frame );

	if( pPacket->isAccelExceeded() )
	{
		emit accelExceeded();
	}
}
// ---------------------------------------------------------------------------


// ------------------- GAutoClient::process_PACKET_EVENT_TRIGGER() -----------
void GAutoClient::process_PACKET_EVENT_TRIGGER( GAutoEventTrigger * pEventTrigger )
{
	// check pointer
	if( !pEventTrigger ) return;

	
	emit reportObject( pEventTrigger->message(),
					   pEventTrigger->eventType() == GAutoEventTrigger::EVENT_SPEEDING
					 );
}
// ---------------------------------------------------------------------------


// ----------------- GAutoClient::stopReceiving() ----------------------------
void GAutoClient::stopReceiving()
{
		// stop receiving
	_udpReceiver.stopReceiving();
		// tell camera to stop sending stuff
	GUDPHostInfo udpHostInfo( QHostAddress(), 0 );
	sendPacket( udpHostInfo );
}
// ---------------------------------------------------------------------------


// ------------------ GAutoClient::startReceiving() --------------------------
void GAutoClient::startReceiving()
{
		// send nessesarry udp listener info 
	GUDPHostInfo udpHostInfo( this->localAddress(), UDP_PORT );
	sendPacket( udpHostInfo );
	
		// if receiving than stop 
	if( _udpReceiver.isReceiving() )
	{
		_udpReceiver.stopReceiving();
	}
		
	// start receiving	
	_udpReceiver.startReceiving( UDP_PORT );
}
// ---------------------------------------------------------------------------

// ------------------------------ Disconnect from host ---------------------------------------------
void GAutoClient::disconnectFromHost()
{
	// stop receiving when disconnecting
	stopReceiving();
	
	GNetClient::disconnectFromHost();
}

// ---------------------------------------------------------------------------


// eof
